package com.simen.downloader;

import android.os.Handler;
import android.util.Log;

import com.simen.downloader.exception.ThreadCancelledException;
import com.simen.downloader.exception.ToastException;
import com.simen.downloader.model.LocalFile;
import com.simen.downloader.util.AppLog;
import com.simen.downloader.util.FileStorageUtils;
import com.simen.downloader.util.Utils;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by zhangming on 16/4/11.
 */
final class DownloadThread_backup implements Runnable {
    public final static String TAG = DownloadThread_backup.class.getCanonicalName();
    public final static int CONNECTTIMEOUT = 8000, READTIMEOUT = 5000;
    public final static int PROGRESS_INTERVAL = 500;
    public final static String HTTPPOST = "POST", HTTPGET = "GET";
    private final Handler handler;
    private final DownloadInfor downloadInfor;
    private final String key;
    private final String logFilePath, filePath;
    private final DownloadEngine engine;

    public DownloadThread_backup(DownloadEngine engine, DownloadInfor downloadInfor, Handler handler) {
        this.engine = engine;
        this.handler = handler;
        this.key = downloadInfor.key;
        this.downloadInfor = downloadInfor;
        this.logFilePath = FileStorageUtils.getDefaultLogFilePath(downloadInfor.downloadOptions
                .fileDir, downloadInfor.key).getPath();
        this.filePath = FileStorageUtils.getDefaultFilePath(downloadInfor.downloadOptions
                .fileDir, downloadInfor.key).getPath();
    }

    @Override
    public void run() {
        if (waitIfPaused()) {
            return;
        }
        HttpURLConnection conn = null;
        DataOutputStream out = null;
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        long writeLength = 0, filelength = 0;
        long createtime = 0, lasttime = 0;
        int progress = 0;
        String finalUrl = null, fileName = null;
        boolean resume = false;

        ReentrantLock lock = engine.getLockForUri(key);
        if (lock.isLocked()) {
            AppLog.dLog("the lock has been locked " + key);
        }

        lock.lock();

        try {
            URL url = new URL(key);
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(CONNECTTIMEOUT); //设置连接超时
            conn.setReadTimeout(READTIMEOUT);
            conn.setRequestMethod(HTTPGET); // 设定请求方式
            //支持重定向
            conn.setInstanceFollowRedirects(true);
            conn.connect();
            int responseCode = conn.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                finalUrl = conn.getURL().toString();
//                if (!conn.getURL().sameFile(url)) {
//                    AppLog.dLog("重定向下载: " + finalUrl);
//                }
                fileName = Utils.getFileNameByUrl(finalUrl);

                checkThreadInterrupted();
                checkListenerRecycled();

                filelength = conn.getContentLength();
                if (filelength == -1) {
                    throw new ToastException("下载文件无效");
                }

                AppLog.dLog("新的文件长度: " + filelength);

                File realFile = new File(filePath);
                StorageFile logStorageFile = (StorageFile) DownloadHelper.getFileWriter(new File(logFilePath));
                if (Utils.fileExist(realFile) && logStorageFile != null && key.equals
                        (logStorageFile.getFileUrl()) && finalUrl.equals(logStorageFile.getFileRealUrl())) {
                    long reallength = realFile.length();
                    if (reallength == filelength) {

                        writeLength = filelength;
                        progress = 100;

                        //下载文件已存在,已完成
                        if (!isListenerRecycled()) {
                            final LocalFile localFile = new LocalFile(filePath, finalUrl);
                            handler.postDelayed(new DownloadService.MessageRunnable(getStatus(finalUrl, createtime,
                                    lasttime, writeLength, filelength, progress, fileName, filePath)) {
                                @Override
                                public void run() {
                                    downloadInfor.loader.get().onProgress(key, 100);
                                    downloadInfor.loader.get().onComplete(key, localFile);
                                }
                            }, 0);
                        } else {
                            AppLog.dLog("文件已下载完成,但回调对象已被回收,");
                        }
                    } else {
//                        resume = true;
//                        StorageFile storageFile = new StorageFile(key, finalUrl, createtime,
//                                lasttime, reallength, filelength, progress, fileName, filePath);
//                        postResumeThread(engine, new DownloadResumeThread(engine, downloadInfor,
//                                handler, storageFile));
                    }
                    return;
                }

                checkThreadInterrupted();
                checkListenerRecycled();

                inputStream = conn.getInputStream();
                fileOutputStream = new FileOutputStream(filePath);

                byte[] data = new byte[1024];
                int read = 0;
                int newprogress = 0;
                long interval = System.currentTimeMillis();
                while ((read = inputStream.read(data, 0, 1024)) != -1) {
                    writeLength += read;
                    fileOutputStream.write(data, 0, read);

                    progress = (int) (writeLength * 1.0 / filelength * 100);
                    if (progress % 5 == 0) {
                        // FIXME: 16/5/13 防止匿名线程的无限下载
                        checkThreadInterrupted();
                    }

                    ReentrantReadWriteLock.ReadLock readLock = downloadInfor.run.getReadLock();
                    readLock.lock();
                    boolean workStatus = downloadInfor.run.get();
                    readLock.unlock();

                    if (!workStatus) {
                        break;
                    }

                    if (isListenerRecycled()) {
                        continue;
                    }

                    if (progress == 100 || (newprogress != progress && (System.currentTimeMillis()
                            - interval > PROGRESS_INTERVAL))) {
                        //刷新进度
                        newprogress = progress;
                        interval = System.currentTimeMillis();
                        // TODO: 16/5/12 考虑到并发性问题,可将判断放到此处
//                        if (!isListenerRecycled()) {
                        handler.obtainMessage(0, getStatus(finalUrl, createtime,
                                lasttime, writeLength, filelength, progress, fileName, filePath)).sendToTarget();
//                        }
                    }
                }

                if (read == -1) {
                    //下载完成
                    final LocalFile localFile = new LocalFile(filePath, finalUrl);
                    if (!isListenerRecycled()) {
                        handler.postDelayed(new DownloadService.MessageRunnable(getStatus(finalUrl, createtime,
                                lasttime, writeLength, filelength, progress, fileName, filePath)) {
                            @Override
                            public void run() {
                                downloadInfor.loader.get().onComplete(key, localFile);
                            }
                        }, 0);
                    }
                }
            } else {
                throw new ToastException("HTTP错误码：" + responseCode);
            }
        } catch (ToastException e) {
            e.printStackTrace();
        } catch (ThreadCancelledException e) {
            e.printStackTrace();
        } catch (SocketTimeoutException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception ee) {
            }
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception ee) {
            }

            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();

                    if (finalUrl != null) {
                        //保存下载信息
                        ObjectOutputStream objectOutputStream = null;
                        try {
                            objectOutputStream = new ObjectOutputStream(new FileOutputStream(this.logFilePath));
                            objectOutputStream.writeObject(new StorageFile(key, finalUrl, createtime,
                                    lasttime, writeLength, filelength, progress, fileName, filePath));
                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {
                            try {
                                if (objectOutputStream != null) {
                                    objectOutputStream.close();
                                }
                            } catch (IOException e) {

                            }
                        }
                    }
                }
            } catch (Exception ee) {
                ee.printStackTrace();
            }

            if (conn != null) {
                conn.disconnect();
            }

            lock.unlock();

            if (!isListenerRecycled()) {
                if (progress != 100 && !resume) { //任务停止运行
                    handler.postDelayed(new DownloadService.MessageRunnable(getStatus(finalUrl, createtime,
                            lasttime, writeLength, filelength, progress, fileName, filePath)) {
                        @Override
                        public void run() {
                            downloadInfor.loader.get().onPause(key);
                        }
                    }, 0);
                } else if (progress == 100 && !resume) {
                    //下载完成
                    final LocalFile localFile = new LocalFile(filePath, finalUrl);
                    handler.postDelayed(new DownloadService.MessageRunnable(getStatus(finalUrl, createtime,
                            lasttime, writeLength, filelength, progress, fileName, filePath)) {
                        @Override
                        public void run() {
                            downloadInfor.loader.get().onProgress(key, 100);
                            downloadInfor.loader.get().onComplete(key, localFile);
                        }
                    }, 0);
                }
            }
        }
    }

    public static void postResumeThread(DownloadEngine engine, Runnable runnable) {
        engine.executeFileThread(runnable);
    }

    private DownloadService.DownloadStatus getStatus(String fileRealUrl, long createtime,
                                                     long lasttime, long current, long total, int
                                                             progress, String fileName, String filePath) {
//        StorageFile storageFile = new StorageFile(key, fileRealUrl, createtime,
//                lasttime, current, total, progress, fileName, filePath);
//        return new DownloadService.DownloadStatus(storageFile, downloadInfor.loader.get());
        return null;
    }

    private boolean waitIfPaused() {
        AtomicBoolean pause = engine.getPause();
        if (pause.get()) {
            synchronized (engine.getPauseLock()) {
                if (pause.get()) {
                    try {
                        engine.getPauseLock().wait();
                    } catch (InterruptedException e) {
                        return true;
                    }
                }
            }
        }
        return isListenerRecycled();
    }

    private void checkListenerRecycled() throws ThreadCancelledException {
        if (isListenerRecycled()) {
            throw new ThreadCancelledException();
        }
    }

    private boolean isListenerRecycled() {
        return downloadInfor == null || downloadInfor.isCollected();
    }

    /**
     * @throws ThreadCancelledException if current task was interrupted
     */
    private void checkThreadInterrupted() throws ThreadCancelledException {
        if (isThreadInterrupted()) {
            throw new ThreadCancelledException();
        }
    }

    /**
     * @return <b>true</b> - if current task was interrupted; <b>false</b> - otherwise
     */
    private boolean isThreadInterrupted() {
        if (Thread.interrupted()) {
            Log.d(TAG, "task has been interrupted,for key:" + key);
            return true;
        }
        return false;
    }

}
